import time
from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union

from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
from sqlalchemy.orm import Session
from sqlalchemy import and_, func
from app.db.base_class import Base
from app.schemas.base import ListFilterSchema,RespListSchema,ListArgsSchema,ListKeySchema,ListOrderSchema
import math
from app.utils.obj2dict import obj2dict

ModelType = TypeVar("ModelType", bound=Base)
CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel)
UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel)

PAGE_RANGE_DISPLAYED = 10
MARGIN_PAGES_DISPLAYED = 2
SHOW_FIRST_PAGE_WHEN_INVALID = False

class Page(object):

    def __init__(self, items, page, page_size, total):
        self.items = items
        self.previous_page = None
        self.next_page = None
        self.page = page
        self.page_size = page_size
        self.has_previous = page > 1
        if self.has_previous:
            self.previous_page = page - 1
        previous_items = (page - 1) * page_size
        self.has_next = previous_items + len(items) < total
        if self.has_next:
            self.next_page = page + 1
        self.total = total
        self.pages = int(math.ceil(total / float(page_size)))

    def _pages(self):
        if self.pages <= PAGE_RANGE_DISPLAYED:
            return range(1, self.pages + 1)
        result = []
        left_side = PAGE_RANGE_DISPLAYED / 2
        right_side = PAGE_RANGE_DISPLAYED - left_side
        if self.page > self.pages - PAGE_RANGE_DISPLAYED / 2:
            right_side = self.pages - self.page
            left_side = PAGE_RANGE_DISPLAYED - right_side
        elif self.page < PAGE_RANGE_DISPLAYED / 2:
            left_side = self.page
            right_side = PAGE_RANGE_DISPLAYED - left_side
        for page in range(1, self.pages + 1):
            if page <= MARGIN_PAGES_DISPLAYED:
                result.append(page)
                continue
            if page > self.pages - MARGIN_PAGES_DISPLAYED:
                result.append(page)
                continue
            if (page >= self.page - left_side) and (page <= self.page + right_side):
                result.append(page)
                continue
            if result[-1]:
                result.append(None)
        return result

    def page_render(self):
        """

        """

def paginate(query, page, page_size):
    if page <= 0:
        page=1
    if page_size <= 0:
        page_size=50
    items = query.limit(page_size).offset((page - 1) * page_size).all()
    # We remove the ordering of the query since it doesn't matter for getting a count and
    # might have performance implications as discussed on this Flask-SqlAlchemy issue
    # https://github.com/mitsuhiko/flask-sqlalchemy/issues/100
    total = query.count()
    return Page(items, page, page_size, total)

class BaseService(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
    Model = None
    def __init__(self, model: Type[ModelType]):
        """
        CRUD object with default methods to Create, Read, Update, Delete (CRUD).
        **Parameters**
        * `model`: A SQLAlchemy model class
        * `schema`: A Pydantic model (schema) class
        """
        self.model = model

    def get(self, db: Session, id: Any,for_update=False,**kwargs) -> Optional[ModelType]:
        if kwargs:
            kwargs['id'] = id
            kwargs['is_deleted'] = 0
            if for_update:
                return db.query(self.model).filter_by(**kwargs).with_for_update().first()
            else:
                return db.query(self.model).filter_by(**kwargs).first()
        else:
            if for_update:
                return db.query(self.model).filter(self.model.id == id,self.model.is_deleted==0).with_for_update().first()
            else:
                return db.query(self.model).filter(self.model.id == id,self.model.is_deleted==0).scalar()
    def get_by_key(self, db: Session, key:Any,value:Any) -> Optional[ModelType]:
        return db.query(self.model).filter({key:value}).first()
    def get_multi(self, db: Session, *, skip: int = 0, limit: int = 100) -> List[ModelType]:
        return db.query(self.model).offset(skip).limit(limit).all()

    def create(self, db: Session, *, obj_in: CreateSchemaType) -> ModelType:
        obj_in_data = jsonable_encoder(obj_in)
        db_obj = self.model(**obj_in_data)  # type: ignore
        db.add(db_obj)
        db.commit()
        db.refresh(db_obj)
        return db_obj

    def update(self,db: Session, *, db_obj: ModelType,obj_in: Union[UpdateSchemaType, Dict[str, Any]]) -> ModelType:
        obj_data = jsonable_encoder(db_obj)
        if isinstance(obj_in, dict):
            update_data = obj_in
        else:
            update_data = obj_in.dict(exclude_unset=True)
        for field in obj_data:
            if field in update_data:
                setattr(db_obj, field, update_data[field])
        db.add(db_obj)
        db.commit()
        db.refresh(db_obj)
        return db_obj

    def remove(self, db: Session, *, id: int) -> ModelType:
        obj = db.query(self.model).get(id)
        db.delete(obj)
        db.commit()
        return obj

    def remove_by_key(self, db: Session,key:Any,value:Any) -> ModelType:
        db.query(self.model).filter({key:value}).delete(synchronize_session=False)
        db.commit()

    def logistic_delete(self,db: Session,db_obj: ModelType):
        '''
        逻辑删除
        '''
        db_obj.is_deleted=int(time.time())
        db.commit()
        db.refresh(db_obj)
        return db_obj

    def _handle_list_filters(self, args_filters: ListFilterSchema):
        """
        处理list接口传入的过滤条件
        :param args_filters: 传入过滤条件
        :return: 转换后的sqlalchemy过滤条件
        """
        filters = []
        if args_filters:
            for item in args_filters:
                if hasattr(self.model, item.key):
                    attr = getattr(self.model, item.key)
                    if item.condition == '=':
                        filters.append(attr == item.value)
                    elif item.condition == '!=':
                        filters.append(attr != item.value)
                    elif item.condition == '<':
                        filters.append(attr < item.value)
                    elif item.condition == '>':
                        filters.append(attr > item.value)
                    elif item.condition == '<=':
                        filters.append(attr <= item.value)
                    elif item.condition == '>=':
                        filters.append(attr >= item.value)
                    elif item.condition == 'like':
                        filters.append(attr.like(f'{item.value}%'))
                    elif item.condition == 'in':
                        filters.append(attr.in_(item.value.split(',')))
                    elif item.condition == '!in':
                        filters.append(~attr.in_(item.value.split(',')))
                    elif item.condition == 'null':
                        filters.append(attr.is_(None))
                    elif item.condition == '!null':
                        filters.append(~attr.isnot(None))
        return filters

    def read_backend_list(self,db: Session,args: ListArgsSchema,**kwargs) -> RespListSchema:
        """
        读取数据列表
        :param args: 聚合参数，详见：ListArgsSchema
        :return: 返回数据列表结构，详见：RespListSchema
        """
        # 定义：query过滤条件
        filters = []
        # 判断：是否包含已软删除的数据
        if args.is_deleted != 'all':
            filters.append(self.model.is_deleted == 0)
        # 判断：是否限制指定用户的数据
        if args.user_id:
            filters.append(self.model.user_id == args.user_id)
        # 增加：传入调整
        filters.extend(self._handle_list_filters(args.filters))
        # 判断：是否进行关键词搜索
        if args.keywords and hasattr(self.model, 'search'):
            filters.append(and_(*[self.model.search.like('%' + kw + '%') for kw in args.keywords.split(' ')]))

        # 执行：数据检索
        query = db.query(self.model).filter(*filters)
        count = query.count()

        # 判断： 结果数，是否继续查询
        if count > 0:
            orders = self._handle_list_orders(args.orders)
            query = query.order_by(*orders)#.offset((args.page - 1) * args.size).limit(args.size).all()
        return paginate(query, args.page,args.size)
        # else:
        #     obj_list = []
        # # 构造：返回结构
        # resp = RespListSchema()
        # resp.page = args.page
        # resp.size = args.size
        # resp.count = count
        # resp.page_count = math.ceil(count / args.size)  # 计算总页数
        # resp.data = [obj.to_dict() for obj in obj_list]#self._handle_list_keys(args.keys, obj_list)  # 处理list
        # return resp

    def read_list(self, db: Session, args: ListArgsSchema):
        """
        读取数据列表
        :param args: 聚合参数，详见：ListArgsSchema
        :return: 返回数据列表结构，详见：RespListSchema
        """
        # 定义：query过滤条件
        filters = []
        # 判断：是否包含已软删除的数据
        if args.is_deleted != 'all':
            filters.append(self.model.is_deleted == 0)
        # 判断：是否限制指定用户的数据
        if args.user_id:
            filters.append(self.model.user_id == args.user_id)
        # 增加：传入调整
        filters.extend(self._handle_list_filters(args.filters))
        # 判断：是否进行关键词搜索
        if args.keywords and hasattr(self.model, 'search'):
            filters.append(and_(*[self.model.search.like('%' + kw + '%') for kw in args.keywords.split(' ')]))

        # 执行：数据检索
        query = db.query(self.model).filter(*filters)
        count = query.count()

        # 判断： 结果数，是否继续查询
        if count > 0:
            orders = self._handle_list_orders(args.orders)
            obj_list = query.order_by(*orders).offset((args.page - 1) * args.size).limit(args.size).all()
        else:
            obj_list = []
        # 构造：返回结构
        resp = RespListSchema()
        resp.page = args.page
        resp.size = args.size
        resp.count = count
        resp.page_count = math.ceil(count / args.size)  # 计算总页数
        resp.data = [obj.to_dict() for obj in obj_list]  # self._handle_list_keys(args.keys, obj_list)  # 处理list
        return resp


    def _handle_list_orders(self, args_orders: List[ListOrderSchema]):
        """
        处理list接口传入的排序条件
        :param args_orders: 传入排序条件
        :return: 转换后的sqlalchemy排序条件
        """
        orders = []
        if not args_orders:
            args_orders=[]
            if hasattr(self.model, 'create_at'):
                args_orders.append(ListOrderSchema( key='create_at',condition='desc'))

        for item in args_orders:
            if hasattr(self.model, item.key):
                attr = getattr(self.model, item.key)
                if item.condition == 'desc':
                    orders.append(attr.desc())
                elif item.condition == 'acs':
                    orders.append(attr)
                elif item.condition == 'rand':  # 随机排序
                    orders.append(func.rand())
        return orders

    def _handle_list_keys(self, args_keys: ListKeySchema, obj_list: List):
        """
        处理list返回数据，根据传入参数keys进行过滤
        :param args_keys: 传入过滤字段
        :return: 转换后的list数据，数据转为dict类型
        """
        keys = []
        if args_keys:
            for item in args_keys:
                if hasattr(self.model, item.key):
                    keys.append(item)
        resp_list = []
        for obj in obj_list:
            dict_1 = obj2dict(obj)
            # 判断：keys存在，不存在则返回所有字段
            if keys:
                dict_2 = {}
                for item in keys:
                    if item.rename:
                        dict_2[item.rename] = dict_1[item.key]
                    else:
                        dict_2[item.key] = dict_1[item.key]
            else:
                dict_2 = dict_1
            resp_list.append(dict_2)
        return resp_list

    def create(self,db: Session, model: Model):
        """
        创建一条数据
        :param model: 数据模型实例
        """
        db.add(model)
        db.flush()

    def query_by_id(self, db: Session, id: int, user_id: int = None, is_deleted: int = 0) -> Model:
        """
        读取一条数据
        :param id: 数据id
        :param user_id: 用户id
        :param is_deleted: 是否为已删除数据
        :return: 数据模型实例
        """
        # 定义：query过滤条件
        filters = []
        # 判断：软删标记
        if is_deleted == 1:
            filters.append(self.model.is_deleted == 1)
        elif is_deleted == 2:
            pass
        else:
            filters.append(self.model.is_deleted == 0)
        # 判断：是否限制指定用户的数据
        if user_id:
            filters.append(self.model.user_id == user_id)
        return db.query(self.model).filter(
            self.model.id == id,
            *filters
        ).scalar()

    def query_by_key(self,db:Session,args_filters: ListFilterSchema,is_deleted: int = 0) -> Model:
        """
        读取一条数据
        :param id: 数据id
        :param user_id: 用户id
        :param is_deleted: 是否为已删除数据
        :return: 数据模型实例
        """

        # 定义：query过滤条件
        filters = []
        # 判断：软删标记
        if is_deleted == 1:
            filters.append(self.model.is_deleted == 1)
        elif is_deleted == 2:
            pass
        else:
            filters.append(self.model.is_deleted == 0)
        # 判断：是否限制指定用户的数据
        filters.extend(self._handle_list_filters(args_filters))
        return db.query(self.model).filter(*filters).scalar()

    def update(self,db:Session,model: Model):
        """
        更新一条数据
        :param model: 数据模型实例
        :return:
        """
        db.add(model)
        db.flush()

    def delete(self,db:Session,model: Model):
        """
        删除一条数据，软删除
        :param model: 数据模型实体
        """
        model.is_deleted = int(time.time())
        self.update(db,model)
